1. Object

Object 是根父类,如何理解?

从继承的角度

  1. 如果一个类没有显示声明它的父类,那么它的父类就是 Object
  2. 说明所有类,所有对象都拥有 Object 类中的方法
  3. 每一个类的构造器,往上追踪都会调用到 Object 类中的无参构造

从多态角度

  1. Object 的变量(包括行参)可以接收任意类型的对象
  2. Object 数组,可以存储任意类型的对象

Object 中部分方法介绍

  1. getClass, 返回此 Object 的运行时类型。
  2. toString, 当打印一个对象时,默认调用这个对象的 toString() 方法。当对象与一个String 的值进行 + 操作时,也会调用 toString() 方法。
  3. hashCode, 返回对象的 hash 码
  4. equals, 指示其他对象是否与此对象相等。如果一个类没有重写 equals 方法,那么效果和 == 是一样的,比较的是两个对象的地址。如果不想使用 equals 来比较地址,而是比较属性的内容,那么需要重写 equals 方法。
  5. finalize, 当垃圾回收器确定不存在该对象的更多应用时,由对象的垃圾回收器调用此方法。

非静态代码块

特点

  1. 在创建对象的时候自动执行,每创建一个对象就执行一次。如果有多个非静态代码块,那么按顺序执行。
  2. 先于构造器执行。
  3. 与属性的显示赋值相比,他俩谁在前面谁就先执行。

实例初始化方法

  1. .java 代码编译为 .class 时,会把代码重新组装,如果类中有 n 个构造器,那么就会组装出 n 个实例初始化方法
  2. 实例初始化方法由三部分组成,分别是 (a)属性的显式赋值语句, (b)非静态代码块的语句, (c)构造器的语句。组装的顺序是(a) (b) 谁在前就谁先组装,(c) 排在最后。
  3. 创建子类对象的时候会先调用父类的实例初始化方法。

用实例初始化方法解释下面代码的执行顺序

Person.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.rexyan.object;

public class Person {
private String name = getName();

{
System.out.println("父类非静态代码块");
}

Person(){
super();
System.out.println("父类无参构造");
}

Person(String name){
this.name = name;
System.out.println("父类有参构造");
}

public String getName(){
System.out.println("父类属性显式赋值");
return "test";
}
}

Student.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.rexyan.object;

public class Student extends Person{
private int age = getAge();

{
System.out.println("子类非静态代码块");
}

Student(){
super();
System.out.println("子类无参构造");
}

Student(int age){
this.age = age;
System.out.println("子类有参构造");
}

public int getAge(){
System.out.println("子类属性显式赋值");
return 20;
}

// main
public static void main(String[] args){
Student s1 = new Student();
}
}

输出

1
2
3
4
5
6
父类属性显式赋值  // 属性的显式赋值在静态代码块前面,所以先执行,即 (a) (b) 谁在前就谁先组装
父类非静态代码块
父类无参构造 // 最后是构造
子类属性显式赋值 // 父类结束后才是子类
子类非静态代码块
子类无参构造

解释: 实例初始化方法由三部分组成,分别是 (a)属性的显式赋值语句, (b)非静态代码块的语句, (c)构造器的语句。组装的顺序是(a) (b) 谁在前就谁先组装,(c) 排在最后。创建子类对象的时候会先调用父类的实例初始化方法。

保持父类不变,将子类改为如下内容:

Person.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.rexyan.object;

public class Person {
private String name = getName();

{
System.out.println("父类非静态代码块");
}

Person(){
super();
System.out.println("父类无参构造");
}

Person(String name){
this.name = name;
System.out.println("父类无参构造");
}

public String getName(){
System.out.println("父类属性显式赋值");
return "test";
}
}

Student.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.rexyan.object;

public class Student extends Person{
private String name = getName();

{
System.out.println("子类非静态代码块");
}

Student(){
super();
System.out.println("子类无参构造");
}

Student(String name){
this.name = name;
System.out.println("子类有参构造");
}

public String getName(){ // 重写父类中的 getName 方法。
System.out.println("子类属性显式赋值");
return "test";
}

// main
public static void main(String[] args){
Student s1 = new Student();
}
}

输出

1
2
3
4
5
6
子类属性显式赋值  // 这里执行父类中的 private String name = getName(); 方法。完整的应该是 this.getName()。this 代表的是当前长在创建的对象,现在正在创建的子类对象。那么其实调用的子类中的 getName() 方法。所以输出的是 “子类属性显式赋值”。
父类非静态代码块
父类无参构造
子类属性显式赋值
子类非静态代码块
子类无参构造